/**
 * Copyright  2025, Hai Yue Xing He  ZHAO LIMIN
 *
 * @author              ZHAO LIMIN
 * @version             1.0.1
 * @since               2025-04
 * VertexFormat.ts
 * 顶点格式。
 */
import { Frozen } from './Frozen'
import { defined } from './Defined'
import { DeveloperError } from './DeveloperError'
import { Check } from './Check'
import { IPackable, packs, unpacks } from './Packable'
import { UNION_NUMBER_ARRAY } from './HDataType'
import { IParametersOptions } from './HBase'

export interface IVertexFormat {
  /**
   * When <code>true</code>, the vertex has a 3D position attribute.
   * <p>
   * 64-bit floating-point (for precision).  3 components per attribute.
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  position: boolean
  /**
   * When <code>true</code>, the vertex has a normal attribute (normalized), which is commonly used for lighting.
   * <p>
   * 32-bit floating-point.  3 components per attribute.
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  normal: boolean
  /**
   * When <code>true</code>, the vertex has a 2D texture coordinate attribute.
   * <p>
   * 32-bit floating-point.  2 components per attribute
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  st: boolean
  /**
   * When <code>true</code>, the vertex has a bitangent(双切线) attribute (normalized),
   * which is used for tangent-space effects like bump mapping.
   * <p>
   * 32-bit floating-point.  3 components per attribute.
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  bitangent: boolean
  /**
   * When <code>true</code>, the vertex has a tangent attribute (normalized),
   * which is used for (正切) tangent-space effects like bump mapping.
   * <p>
   * 32-bit floating-point.  3 components per attribute.
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  tangent: boolean
  /**
   * When <code>true</code>, the vertex has an RGB color attribute.
   * <p>
   * 8-bit unsigned byte.  3 components per attribute.
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  color: boolean

  numLiveTriangles: number
  timeStamp: number
  /**
   * 顶点位置
   * 默认是三个一组
   */
  positions: UNION_NUMBER_ARRAY
  vertexTriangles: UNION_NUMBER_ARRAY
}

export class Vertex implements IVertexFormat {
  /**
   * When <code>true</code>, the vertex has a 3D position attribute.
   * <p>
   * 64-bit floating-point (for precision).  3 components per attribute.
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  public position: boolean = true
  /**
   * When <code>true</code>, the vertex has a normal attribute (normalized), which is commonly used for lighting.
   * <p>
   * 32-bit floating-point.  3 components per attribute.
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  public normal: boolean = true
  /**
   * When <code>true</code>, the vertex has a 2D texture coordinate attribute.
   * <p>
   * 32-bit floating-point.  2 components per attribute
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  public st: boolean = false
  /**
   * When <code>true</code>, the vertex has a bitangent(双切线) attribute (normalized),
   * which is used for tangent-space effects like bump mapping(凹凸贴图).
   * <p>
   * 32-bit floating-point.  3 components per attribute.
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  public bitangent: boolean = false
  /**
   * When <code>true</code>, the vertex has a tangent attribute (normalized),
   * which is used for (正切) tangent-space effects like bump mapping.
   * <p>
   * 32-bit floating-point.  3 components per attribute.
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  public tangent: boolean = false
  /**
   * When <code>true</code>, the vertex has an RGB color attribute.
   * <p>
   * 8-bit unsigned byte.  3 components per attribute.
   * </p>
   *
   * @type {boolean}
   *
   * @default false
   */
  public color: boolean = false
  public numLiveTriangles: number = 0
  public timeStamp: number = 0
  /**
   * 顶点位置
   * 默认是三个一组
   */
  public positions: UNION_NUMBER_ARRAY = []
  public vertexTriangles: UNION_NUMBER_ARRAY = []
  /**
   *
   * @param options
   * @returns
   *
   * @constructor
   *
   */
  constructor(options?: IParametersOptions) {
    options = options ?? Frozen.EMPTY_OBJECT
    this.position = options.position ?? false
    this.normal = options.normal ?? false
    this.st = options.st ?? false
    this.bitangent = options.bitangent ?? false
    this.tangent = options.tangent ?? false
    this.color = options.color ?? false
    this.numLiveTriangles = options.numLiveTriangles ?? 0
    this.timeStamp = options.timeStamp ?? 0
    this.positions = options.positions ?? []
    this.vertexTriangles = options.vertexTriangles ?? []
  }
}

export class VertexFormat extends Vertex implements IPackable {
  /**
   * 顶点格式定义了构成顶点的属性。可以提供vertex format
   * 调用@{link Geometry}请求计算某些属性，例如，仅位置，
   * 位置和正常等。
   *
   * @param {object} [options] An object with boolean properties corresponding to
   * VertexFormat properties as shown in the code example.
   *
   * @alias VertexFormat
   * @constructor
   *
   * @example
   * // Create a vertex format with position and 2D texture coordinate attributes.
   * const format = new VertexFormat({
   *   position : true,
   *   st : true
   * });
   *
   * @see Geometry#attributes
   * @see Packable
   */
  constructor(options?: IParametersOptions) {
    options = options ?? Frozen.EMPTY_OBJECT
    super(options)
  }

  /**
   * An immutable vertex format with only a position attribute.
   *
   * @type {VertexFormat}
   * @constant
   *
   * @see VertexFormat#position
   */
  public static readonly POSITION_ONLY = Object.freeze(
    new VertexFormat({
      position: true,
    }),
  )
  /**
   * An immutable vertex format with position and normal attributes.
   * This is compatible with per-instance color appearances like {@link PerInstanceColorAppearance}.
   *
   * @type {VertexFormat}
   * @constant
   *
   * @see VertexFormat#position
   * @see VertexFormat#normal
   */
  public static readonly POSITION_AND_NORMAL = Object.freeze(
    new VertexFormat({
      position: true,
      normal: true,
    }),
  )
  /**
   * An immutable vertex format with position, normal, and st attributes.
   * This is compatible with {@link MaterialAppearance} when {@link MaterialAppearance#materialSupport}
   * is <code>TEXTURED/code>.
   *
   * @type {VertexFormat}
   * @constant
   *
   * @see VertexFormat#position
   * @see VertexFormat#normal
   * @see VertexFormat#st
   */
  public static readonly POSITION_NORMAL_AND_ST = Object.freeze(
    new VertexFormat({
      position: true,
      normal: true,
      st: true,
    }),
  )
  /**
   * An immutable vertex format with position and st attributes.
   * This is compatible with {@link EllipsoidSurfaceAppearance}.
   *
   * @type {VertexFormat}
   * @constant
   *
   * @see VertexFormat#position
   * @see VertexFormat#st
   */
  public static readonly POSITION_AND_ST = Object.freeze(
    new VertexFormat({
      position: true,
      st: true,
    }),
  )
  /**
   * An immutable vertex format with position and color attributes.
   *
   * @type {VertexFormat}
   * @constant
   *
   * @see VertexFormat#position
   * @see VertexFormat#color
   */
  public static readonly POSITION_AND_COLOR = Object.freeze(
    new VertexFormat({
      position: true,
      color: true,
    }),
  )
  /**
   * An immutable vertex format with well-known attributes: position, normal, st, tangent, and bitangent.
   *
   * @type {VertexFormat}
   * @constant
   *
   * @see VertexFormat#position
   * @see VertexFormat#normal
   * @see VertexFormat#st
   * @see VertexFormat#tangent
   * @see VertexFormat#bitangent
   */
  public static readonly ALL = Object.freeze(
    new VertexFormat({
      position: true,
      normal: true,
      st: true,
      tangent: true,
      bitangent: true,
    }),
  )

  /**
   * An immutable vertex format with position, normal, and st attributes.
   * This is compatible with most appearances and materials; however
   * normal and st attributes are not always required.  When this is
   * known in advance, another <code>VertexFormat</code> should be used.
   *
   * @type {VertexFormat}
   * @constant
   *
   * @see VertexFormat#position
   * @see VertexFormat#normal
   */
  public static readonly DEFAULT = VertexFormat.POSITION_NORMAL_AND_ST
  /**
   * The number of elements used to pack the object into an array.
   * @type {number}
   */
  public static readonly packedLength = 6
  public getPackedLength() {
    return VertexFormat.packedLength
  }
  public getRowLength(): number {
    return 1
  }
  public getColumnLength(): number {
    return VertexFormat.packedLength
  }
  /**
   * Stores the provided instance into the provided array.
   *
   * @param {VertexFormat} value The value to pack.
   * @param {number[]} array The array to pack into.
   * @param {number} [startingIndex=0] The index into the array at which to start packing the elements.
   *
   * @returns {number[]} The array that was packed into
   */
  public static pack(
    value: VertexFormat,
    array: UNION_NUMBER_ARRAY,
    startingIndex: number,
  ): UNION_NUMBER_ARRAY {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object('value', value)
    Check.defined('array', array)
    Check.typeOf.tnumber.checkInteger('startingIndex', startingIndex)
    //>>includeEnd('debug');
    startingIndex = startingIndex ?? 0
    packs(value, array, startingIndex)
    return array
  }
  /**
   * Stores the provided instance into the provided array.
   *
   * @param {number[]} array The array to pack into.
   * @param {number} [startingIndex=0] The index into the array at which to start packing the elements.
   *
   */
  public pack(array: UNION_NUMBER_ARRAY, startingIndex: number = 0) {
    //>>includeStart('debug', pragmas.debug);
    if (!defined(array)) {
      throw new DeveloperError('array is required')
    }
    //>>includeEnd('debug');

    startingIndex = startingIndex ?? 0
    array[startingIndex++] = this.position ? 1.0 : 0.0
    array[startingIndex++] = this.normal ? 1.0 : 0.0
    array[startingIndex++] = this.st ? 1.0 : 0.0
    array[startingIndex++] = this.tangent ? 1.0 : 0.0
    array[startingIndex++] = this.bitangent ? 1.0 : 0.0
    array[startingIndex] = this.color ? 1.0 : 0.0
  }
  /**
   * Retrieves an instance from a packed array.
   *
   * @param {number[]} array The packed array.
   * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
   * @param {VertexFormat} [result] The object into which to store the result.
   * @returns {VertexFormat} The modified result parameter or a new VertexFormat instance if one was not provided.
   */
  public static unpack(
    array: UNION_NUMBER_ARRAY,
    startingIndex: number,
    result: VertexFormat,
  ): VertexFormat {
    //>>includeStart('debug', pragmas.debug);
    Check.defined('array', array)
    //>>includeEnd('debug');

    startingIndex = startingIndex ?? 0

    if (!defined(result)) {
      result = new VertexFormat()
    }
    result = unpacks(array, startingIndex, result) as VertexFormat
    return result
  }
  /**
   * Retrieves an instance from a packed array.
   *
   * @param {number[]} array The packed array.
   * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
   */
  public unpack(array: UNION_NUMBER_ARRAY, startingIndex: number = 0) {
    //>>includeStart('debug', pragmas.debug);
    if (!defined(array)) {
      throw new DeveloperError('array is required')
    }
    //>>includeEnd('debug');
    startingIndex = startingIndex ?? 0

    this.position = array[startingIndex++] === 1.0
    this.normal = array[startingIndex++] === 1.0
    this.st = array[startingIndex++] === 1.0
    this.tangent = array[startingIndex++] === 1.0
    this.bitangent = array[startingIndex++] === 1.0
    this.color = array[startingIndex] === 1.0
  }

  /**
   * Duplicates a VertexFormat instance.
   *
   * @param {VertexFormat} vertexFormat The vertex format to duplicate.
   * @param {VertexFormat} [result] The object onto which to store the result.
   * @returns {VertexFormat} The modified result parameter or a new VertexFormat instance if one was not provided. (Returns undefined if vertexFormat is undefined)
   */
  public static clone(
    vertexFormat: VertexFormat,
    result?: VertexFormat,
  ): VertexFormat | undefined {
    if (!defined(vertexFormat)) {
      return undefined
    }
    if (!defined(result)) {
      result = new VertexFormat()
    }

    result!.position = vertexFormat.position
    result!.normal = vertexFormat.normal
    result!.st = vertexFormat.st
    result!.tangent = vertexFormat.tangent
    result!.bitangent = vertexFormat.bitangent
    result!.color = vertexFormat.color
    return result
  }
} //  end class VertexFormat
